package com.first.config.redis.lock;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.first.config.redis.jedis.SpringJedisUtilInterface;


@Aspect
@Component
public class RedisLockAop {
	
//	@Before("@annotation(CacheLock)")
//	 public void requestLimit(JoinPoint point, CacheLock cacheLock)throws Throwable{
//		
//	}
    private static Logger logger = LoggerFactory.getLogger(RedisLockAop.class);
	@Autowired
    private SpringJedisUtilInterface redisUtil;
//	@Autowired
//    private RedisUtil redisUtil;
	/**
	 * 通过前置拦截，拦截所有经过CacheLock注解过的方法
	 * @param point
	 * @param CacheLock
	 * @throws Throwable 
	 */
	@Around("@annotation(LockMethod)")
	public Object requestLimit(ProceedingJoinPoint  joinPoint) throws Throwable{
		Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
	    LockMethod methodLock = method.getAnnotation(LockMethod.class);
	    if(methodLock ==null){
	    	 throw new RedisLockException("配置参数错误");  
	    }
		Object[] args  = joinPoint.getArgs();
		 // 得到被代理的方法
    	//获得方法中参数的注解
		Annotation[][] annotations = method.getParameterAnnotations();
		//根据获取到的参数注解和参数列表获得加锁的参数
		String sunKey = getLockEndKey(annotations,args);
		String key = methodLock.preKey()+"_"+sunKey+"_lock";
		logger.info(key);
		// 超时时间  （最大的等待时间） 
        long maxSleepMills =  methodLock.maxSleepMills(); 
//        获得锁之后最大可持有锁的时间
        int expire = methodLock.expireTime();
//        是否等待
        boolean toWait = methodLock.toWait();
    	boolean lock = false; 
    	Object obj = null; 
    	int radomInt = 0;// 每个线程随机等待时间
    	try {
//    		 说明没有得到锁，等待轮训的去获得锁
    		 while (!lock) {
    			 lock =getLock(key, expire);
    			  // 得到锁，没有人加过相同的锁  
                 if (lock) {
                	 logger.info("线程获得锁开始执行"+"---"+redisUtil);
                     obj = joinPoint.proceed();  
                     break;  
                 }else if(toWait){
                	 radomInt = new Random().nextInt(99);
                	 maxSleepMills =maxSleepMills- radomInt;
                	 logger.info("没获得锁，随机等待毫秒数："+radomInt+"---"+redisUtil);
                	 if(maxSleepMills<=0){
                		 logger.info("获取锁资源等待超时"+"---"+redisUtil);
                		 break;
//                		 throw new CacheLockException("获取锁资源等待超时,等待超时");  
                	 }
                	 TimeUnit.MILLISECONDS.sleep(radomInt);  
                 }else{
                	 break;  
                 }
    		 }
		} catch (Throwable e) {
			e.printStackTrace();
			logger.info(e.getMessage()+"--"+e);
			throw e;
		}finally{
			if(lock){
				logger.info("执行删除操作");
				redisUtil.del(key );//直接删除
			}
		}
    	return obj;
	}

	
	
	
	/**
	 *  从方法参数中找出@lockedComplexOnbject的参数，在redis中取该参数对应的锁
	 * @param annotations
	 * @param args
	 * @return
	 * @throws RedisLockException
	 */
	private String getLockEndKey(Annotation[][] annotations,Object[] args) throws RedisLockException{
		if(null == args || args.length == 0){
			throw new RedisLockException("方法参数为空，没有被锁定的对象");
		}
		
		if(null == annotations || annotations.length == 0){
			throw new RedisLockException("没有被注解的参数");
		}
		 SortedMap<Integer, String> keys = new TreeMap<Integer, String>();
		 String keyString;
		//直接在多个参数上注解
		for(int i = 0;i < annotations.length;i++){
			for(int j = 0;j < annotations[i].length;j++){
				if(annotations[i][j] instanceof Value){
					   Object arg = args[i]; 
					   logger.info("--Value args[i]--"+i+"------"+arg);
				}
				if(annotations[i][j] instanceof LockParameter){//注解为LockedComplexObject
					LockParameter lockParameter = (LockParameter)annotations[i][j];
				    Object arg = args[i]; 
				    logger.info("--lockParameter args[i]--"+i+"------"+arg);
				    try {
				    	if(lockParameter.isSimple()){
				    		keys.put(i, String.valueOf(arg));
				    	}else{
				    		keyString = args[i].getClass().getField(lockParameter.field()).toString();
				    		keys.put(i,keyString);
				    	}
						
					} catch (NoSuchFieldException |SecurityException e) {
						e.printStackTrace();
						throw new RedisLockException("注解对象中没有该属性"+lockParameter.field());
					} 
				}
			
			}
		}
		String sunKey ="";
		if (keys != null && keys.size() > 0) {  
              for (String key : keys.values()) {  
            	  sunKey = sunKey + key;  
              }  
       }  
		return sunKey;
	}
	
	 public boolean getLock(String key ,int expire) throws InterruptedException{
		 if(redisUtil.setnx(key, String.valueOf(expire))==1){//1插入成功且key不存在，0未插入，key存在
         	redisUtil.expired(key, expire);
         	return true;
          }
		 return false;
	 }
}
